#ifndef HXLBMPFILEH
#define HXLBMPFILEH

#include "cstdio"
#include "cmath"
#include "windows.h"
class HXLBMPFILE
{
	BYTE *Imagedata;

public:
	int imagew, imageh;						// 图片的宽度和高度
	int iYRGBnum;							// 1：灰度，3：彩色
	RGBQUAD palette[256];					// 调色板
	BYTE *pDataAt(int h, int Y0R0G1B2 = 0); // 指向图像第h行的位置，YR0G1B2表示灰度=0，R=0，G=1，B=2
	BOOL AllocateMem();						// 为图像分配内存
	BOOL LoadBMPFILE(const char *fname);	// 从硬盘加载图像
	BOOL SaveBMPFILE(const char *fname);	// 将图像保存至硬盘
	HXLBMPFILE();							// 构造函数，初始化
	~HXLBMPFILE();							// 析构函数
};

inline HXLBMPFILE::HXLBMPFILE()
{
	Imagedata = NULL;
	for (int i = 0; i < 256; i++) // 赋予调色板初值
	{
		palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = i;
		palette[i].rgbReserved = 0;
	}
	iYRGBnum = 0;
	imagew = imageh = 0;
}

inline HXLBMPFILE::~HXLBMPFILE()
{
	if (Imagedata)
		delete[] Imagedata;
}

inline BYTE *HXLBMPFILE::pDataAt(int h, int Y0R0G1B2)
{
	if (iYRGBnum <= Y0R0G1B2)
		return NULL;								 // 如果iYRGBnum=0，则为空图像
	int w = imagew * h + Y0R0G1B2 * imagew * imageh; // 定位到h行，行首像素
	// 后面一项的意思是，R、G、B分别存储在三个矩阵中，需要分别调用
	return Imagedata + w;
}

inline BOOL HXLBMPFILE::AllocateMem()
{
	int w = imagew * imageh * iYRGBnum; // w为需要分配的大小
	if (Imagedata)						// 先将Imagedata清空
	{
		delete[] Imagedata;
		Imagedata = NULL;
	}
	Imagedata = new BYTE[w]; // 重新对Imagedata赋值
	if (Imagedata)
		memset(Imagedata, 0, w); // 全部初始化为0
	return (Imagedata != NULL);	 // 成功则为true
}

inline BOOL HXLBMPFILE::LoadBMPFILE(const char *cFilename)
{
	FILE *f;
	if (strlen(cFilename) < 1) // 文件不存在
		return FALSE;
	f = fopen(cFilename, "r+b"); // 以二进制只读打开
	if (f == NULL)
		return FALSE;
	BITMAPFILEHEADER fh;						// 文件头
	BITMAPINFOHEADER ih;						// 信息头
	fread(&fh, sizeof(BITMAPFILEHEADER), 1, f); // 从文件中读取文件头
	if (fh.bfType != 0x4d42)					// 如果不是"BM"文件，直接退出
	{
		fclose(f);
		return FALSE;
	}
	fread(&ih, sizeof(BITMAPINFOHEADER), 1, f);		   // 从文件中读取信息头
	if ((ih.biBitCount != 8) && (ih.biBitCount != 24)) // 如果不是8位灰度图，或24位彩图，则退出
	{
		fclose(f);
		return FALSE;
	}
	iYRGBnum = ih.biBitCount / 8; // 1为灰度图，3为彩图
	imagew = ih.biWidth;
	imageh = ih.biHeight;
	if (!AllocateMem()) // 如果分配内存失败，退出
	{
		fclose(f);
		return FALSE;
	}
	if (iYRGBnum == 1) // 灰度图需要调色板
		fread(palette, sizeof(RGBQUAD), 256, f);
	fseek(f, fh.bfOffBits, SEEK_SET);				 // 指向像素数据包
	int w4b = (imagew * iYRGBnum + 3) / 4 * 4, i, j; // w4b为像素数据包大小
	BYTE *ptr;
	ptr = new BYTE[w4b];
	if (ptr == NULL)
	{
		fclose(f);
		return FALSE;
	}
	if (iYRGBnum == 1)
	{
		for (i = imageh - 1; i >= 0; i--) // 从最下面开始往上拷贝字节
		{
			fread(ptr, w4b, 1, f);
			memmove(pDataAt(i), ptr, imagew); // memmove(des,src,count)
		}
	}
	if (iYRGBnum == 3)
	{
		for (i = imageh - 1; i >= 0; i--)
		{
			fread(ptr, w4b, 1, f);
			for (j = 0; j < imagew; j++) // 分别读取R、G、B（注意磁盘里反储）
			{
				*(pDataAt(i, 0) + j) = *(ptr + j * 3 + 2);
				*(pDataAt(i, 1) + j) = *(ptr + j * 3 + 1);
				*(pDataAt(i, 2) + j) = *(ptr + j * 3 + 0);
			}
		}
	}
	delete[] ptr;
	fclose(f);
	return TRUE;
}

inline BOOL HXLBMPFILE::SaveBMPFILE(const char *cFilename)
{
	if (!Imagedata)
		return FALSE;
	FILE *f;
	if (strlen(cFilename) < 1)
		return FALSE;
	f = fopen(cFilename, "w+b"); //以二进制写入打开
	if (f == NULL)
		return FALSE;
	BITMAPFILEHEADER fh;
	BITMAPINFOHEADER ih;
	memset(&ih, 0, sizeof(BITMAPINFOHEADER));
	fh.bfType = 0x4d42;
	fh.bfReserved1 = fh.bfReserved2 = 0;
	fh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + ((iYRGBnum == 1) ? 256 * sizeof(RGBQUAD) : 0);
	ih.biSize = 40;
	ih.biPlanes = 1;
	ih.biWidth = imagew;
	ih.biHeight = imageh;
	ih.biBitCount = 8 * iYRGBnum;
	int w4b = (imagew * iYRGBnum + 3) / 4 * 4;
	ih.biSizeImage = ih.biHeight * w4b;
	fh.bfSize = fh.bfOffBits + ih.biSizeImage;
	fwrite(&fh, sizeof(BITMAPFILEHEADER), 1, f); // fwrite(*ptr, size, nmemb,File *stream)
	fwrite(&ih, sizeof(BITMAPINFOHEADER), 1, f);
	if (iYRGBnum == 1)
		fwrite(palette, sizeof(RGBQUAD), 256, f);
	BYTE *ptr;
	int i, j;
	ptr = new BYTE[w4b];
	if (ptr == NULL)
	{
		fclose(f);
		return FALSE;
	}
	memset(ptr, 0, w4b);
	if (iYRGBnum == 1)
	{
		for (i = ih.biHeight - 1; i >= 0; i--)
		{
			memmove(ptr, pDataAt(i), ih.biWidth);
			fwrite(ptr, w4b, 1, f);
		}
	}
	if (iYRGBnum == 3)
	{
		for (i = ih.biHeight - 1; i >= 0; i--)
		{
			for (j = 0; j < ih.biWidth; j++)
			{
				*(ptr + j * 3 + 0) = *(pDataAt(i, 2) + j);
				*(ptr + j * 3 + 1) = *(pDataAt(i, 1) + j);
				*(ptr + j * 3 + 2) = *(pDataAt(i, 0) + j);
			}
			fwrite(ptr, w4b, 1, f);
		}
	}
	delete[] ptr;
	fclose(f);
	return TRUE;
}

#endif